#include <stdio.h>
#include <stdlib.h>
#include <string.h>

enum opcodes {
	
	LOAD,
	STORE,
	
	PUSH,
	POP,
	
	NEG,
	ADD,
	SUB,
	MUL,
	DIV,
	MOD,
	IDIV,

	NOT,
	NOR,
	AND,
	OR,
	XOR,
	
	EQU,
	NEQ,
	GT,
	LT,
	GE,
	LE,

	CALL,
	RET,
};

#define TAU_FALSE	0
#define TAU_TRUE	1
#define TAU_NUM_GLOBAL_START_BINS	1023

int binSizes[] = {
	89,
	179,
	359,
	719,
	1439,
	2879,
	5759,
	11519,
	23039,
	46079,
	92159
};


typedef struct _TauDispatchTable	TauDispatchTable;

typedef struct _TauValue			TauValue;

typedef struct _TauComplex			TauComplex;
typedef struct _TauString			TauString;
typedef struct _TauArray			TauArray;
typedef struct _TauHash				TauHash;
typedef struct _TauMatrix			TauMatrix;
typedef struct _TauObject			TauObject;
typedef struct _TauRoutine			TauRoutine;

typedef struct _TauChunk			TauChunk;
typedef struct _TauFrame			TauFrame;
typedef struct _TauGlobal			TauGlobal;
typedef struct _TauState			TauState;

typedef struct _TauTypeRegistry		TauTypeRegistry;

#define TAU_INTEGER(x)	x->data.i
#define TAU_BOOLEAN(x)	x->data.b
#define TAU_DOUBLE(x)	x->data.d
#define TAU_COMPLEX(x)	x->data.c
#define TAU_STRING(x)	x->data.s
#define TAU_OBJECT(x)	x->data.o
#define TAU_ROUTINE(x)	x->data.r

#define T_NULL			0
#define T_INTEGER		1
#define T_BOOLEAN		2
#define T_DOUBLE		3
#define T_COMPLEX		4
#define T_STRING		5
#define T_OBJECT		6
#define T_ROUTINE		7
#define T_UNDEFINED		8

void TauPushValue (TauValue* v);
void TauPopValue (TauValue* v);

// VM Instructions

void TauLOD		(TauState* s, unsigned char rd, unsigned short rx);
void TauSTO		(TauState* s, unsigned char rd, unsigned short rx);

void TauNEG		(unsigned char rd, unsigned char rs);
void TauADD		(unsigned char rd, unsigned char rs, unsigned char rt);
void TauSUB		(unsigned char rd, unsigned char rs, unsigned char rt);
void TauMUL		(unsigned char rd, unsigned char rs, unsigned char rt);
void TauDIV		(unsigned char rd, unsigned char rs, unsigned char rt);
void TauMOD		(unsigned char rd, unsigned char rs, unsigned char rt);
void TauIDIV	(unsigned char rd, unsigned char rs, unsigned char rt);

void TauNOT		(unsigned char rd, unsigned char rs);
void TauAND		(unsigned char rd, unsigned char rs, unsigned char rt);
void TauOR		(unsigned char rd, unsigned char rs, unsigned char rt);
void TauXOR		(unsigned char rd, unsigned char rs, unsigned char rt);

struct _TauDispatchTable
{
	// Arithmetic routines
	
	TauValue*	(* neg)		(TauValue* vs);
	TauValue*	(* add)		(TauValue* vs, TauValue* vt);
	TauValue*	(* sub)		(TauValue* vs, TauValue* vt);
	TauValue*	(* mul)		(TauValue* vs, TauValue* vt);
	TauValue*	(* div)		(TauValue* vs, TauValue* vt);
	TauValue*	(* idiv)	(TauValue* vs, TauValue* vt);
	TauValue*	(* mod)		(TauValue* vs, TauValue* vt);
	
	// Other math
	
	TauValue*	(* math)			(TauValue* x, TauValue* y);
	
	// Logic routines
	
	TauValue*	(* lnot)	(TauValue* x);
	TauValue*	(* eq)		(TauValue* x, TauValue* y);
	TauValue*	(* neq)		(TauValue* x, TauValue* y);
	TauValue*	(* gt)		(TauValue* x, TauValue* y);
	TauValue*	(* lt)		(TauValue* x, TauValue* y);	
	TauValue*	(* ge)		(TauValue* x, TauValue* y);
	TauValue*	(* le)		(TauValue* x, TauValue* y);
	
	// Bit routines
	
	TauValue*	(* not)		(TauValue* x);
	TauValue*	(* and)		(TauValue* x, TauValue* y);
	TauValue*	(* or)		(TauValue* x, TauValue* y);
	TauValue*	(* xor)		(TauValue* x, TauValue* y);
	
	TauValue*	(* shl)		(TauValue* a, TauValue* b);
	TauValue*	(* shr)		(TauValue* a, TauValue* b);
	
	// Sequence routines
	
	TauValue*	(* length)			(TauValue* s);
	TauValue*	(* concat)			(TauValue* s, TauValue* t);
	TauValue*	(* repeat)			(TauValue* s, TauValue* t);
	TauValue*	(* index)			(TauValue* s, int i);
	TauValue*	(* slice)			(TauValue* s, int i, int j);
	TauValue*	(* toLower)			(TauValue* s);
	TauValue*	(* toUpper)			(TauValue* s);
	
	TauValue*	(* size)			(TauValue* x);
	TauValue*	(* capacity)		(TauValue* x);
	TauValue*	(* subscript)		(TauValue* self, int i);
	
	// Iterator routines
	
	TauValue*	(* iter)			(TauValue* x);
	TauValue*	(* hasNext)			(TauValue* i);
	TauValue*	(* nextg)			(TauValue* i);	

};

TauDispatchTable* TauMakeDispatchTable (void);

TauValue* TauNullInvalid1	(TauValue* vs);
TauValue* TauNullInvalid2	(TauValue* vs, TauValue* vt);

TauValue* TauIntegerNeg		(TauValue* vs);
TauValue* TauIntegerAdd		(TauValue* vs, TauValue* vt);
TauValue* TauIntegerSub		(TauValue* vs, TauValue* vt);
TauValue* TauIntegerMul		(TauValue* vs, TauValue* vt);
TauValue* TauIntegerDiv		(TauValue* vs, TauValue* vt);
TauValue* TauIntegerMod		(TauValue* vs, TauValue* vt);
TauValue* TauIntegerIdiv	(TauValue* vs, TauValue* vt);
TauValue* TauIntegerNot		(TauValue* vs);
TauValue* TauIntegerAnd		(TauValue* vs, TauValue* vt);
TauValue* TauIntegerOr		(TauValue* vs, TauValue* vt);
TauValue* TauIntegerXor		(TauValue* vs, TauValue* vt);

TauValue* TauBooleanNeg		(TauValue* vs);
TauValue* TauBooleanAdd		(TauValue* vs, TauValue* vt);
TauValue* TauBooleanSub		(TauValue* vs, TauValue* vt);
TauValue* TauBooleanMul		(TauValue* vs, TauValue* vt);
TauValue* TauBooleanDiv		(TauValue* vs, TauValue* vt);
TauValue* TauBooleanMod		(TauValue* vs, TauValue* vt);
TauValue* TauBooleanIdiv	(TauValue* vs, TauValue* vt);
TauValue* TauBooleanNot		(TauValue* vs);
TauValue* TauBooleanAnd		(TauValue* vs, TauValue* vt);
TauValue* TauBooleanOr		(TauValue* vs, TauValue* vt);
TauValue* TauBooleanXor		(TauValue* vs, TauValue* vt);

TauValue* TauDoubleNeg		(TauValue* vs);
TauValue* TauDoubleAdd		(TauValue* vs, TauValue* vt);
TauValue* TauDoubleSub		(TauValue* vs, TauValue* vt);
TauValue* TauDoubleMul		(TauValue* vs, TauValue* vt);
TauValue* TauDoubleDiv		(TauValue* vs, TauValue* vt);
TauValue* TauDoubleMod		(TauValue* vs, TauValue* vt);
TauValue* TauDoubleInvalid1	(TauValue* vs);
TauValue* TauDoubleInvalid2	(TauValue* vs, TauValue* vt);

TauValue* TauComplexNeg			(TauValue* vs);
TauValue* TauComplexAdd			(TauValue* vs, TauValue* vt);
TauValue* TauComplexSub			(TauValue* vs, TauValue* vt);
TauValue* TauComplexMul			(TauValue* vs, TauValue* vt);
TauValue* TauComplexDiv			(TauValue* vs, TauValue* vt);
TauValue* TauComplexInvalid1	(TauValue* vs);
TauValue* TauComplexInvalid2	(TauValue* vs, TauValue* vt);

TauValue* TauStringAdd		(TauValue* vs, TauValue* vt);

// Type registry

struct _TauTypeRegistry
{
	int* id;
	char** name;
};


struct _TauChunk
{
	unsigned int len;
	unsigned char* code;
	
	unsigned short nglobals;
	char** globals;
	
	unsigned short nconstants;
	TauValue** constants;
	
	// where are routines stored?
	unsigned short nroutines;
	TauRoutine* routines;
};

TauChunk* TauLoadChunk (char* filename);


struct _TauFrame
{
	TauValue** regs;
	TauValue** locals;
	int nlocals;
	int nparams;
	
	unsigned char* code;
	int cp;
	
	TauFrame* prev;
	TauFrame* link;
};


TauFrame* TauMakeFrame (void);



struct _TauGlobal
{
	char* name;
	TauValue* value;
	
	TauGlobal* next;
};

TauGlobal* TauMakeGlobal (char* name, int index);

void TauSetGlobal (TauState* s, char* name, TauValue* value);

TauValue* TauGetGlobal (TauState* s, char* name);

int TauDeleteGlobal (TauState* s, char* name);



struct _TauState
{
	int nglobals;
	TauGlobal** globals;
	
	unsigned short paramstacksize;
	TauValue** paramstack;
	int psp;

	TauFrame* fp;
};

TauState* TauMakeState (void);

void TauPushFrame (TauState* s, TauFrame* f);

void TauPopFrame (TauState* s);



struct _TauValue
{
	int type;
	int refs;
	
	union
	{
		int i;
		int b;
		double d;
		TauComplex* c;
		TauString* s;
		TauObject* o;
		TauRoutine* r;
	} data;
};

//TauArray* a;
//TauString* s;
//TauHash* h;
//TauMatrix* m;

struct _TauComplex
{
	double real;
	double imag;
};


struct _TauObject
{
	TauValue* data;
	// properties, attributes, features?
};

struct _TauRoutine
{
	unsigned int len;
	unsigned char* code;
	
	unsigned short nparams;
	char** params;				// Only for debugging
	
	unsigned short nlocals;		// nlocals includes nparams
	char** locals;				// Only for debugging
	
	unsigned short nroutines;
	TauRoutine* routines;
};



struct _TauString
{
	char* data;
	int len;
};

struct _TauArray
{
	TauObject data;
	int capacity;
	int len;
};

struct _TauHash
{
	
	int len;
};



TauValue* TauMakeValue (void);

TauValue* TauMakeNull (void);
TauValue* TauMakeInteger (int i);
TauValue* TauMakeBoolean (int b);
TauValue* TauMakeDouble (double d);
TauValue* TauMakeComplex(double cr, double ci);
TauValue* TauMakeString (void);


// Parameter stack manipulation

TauValue* TauMakeHash (void);

void TauRegisterType(int id, char* name);
